
<!DOCTYPE HTML>

<html>

<head>
	<meta charset="utf-8">
	<title>JSB内存管理（Memory Management of JSB） - 前端艺术</title>
	<meta name="author" content="Nicholas">

	
	<meta name="description" content="JSB内存管理（Memory Management of JSB） 原文地址：http://www.cocos2d-x.org/wiki/Memory_Management_of_JSB 译者：晋文格墨 注：因译者水平有限，如有错误，请批评指正。 JSB的内存管理 基于Cocos2d-x 2. &hellip;">
	

  <!-- http://t.co/dKP3o1e -->
  <meta name="HandheldFriendly" content="True">
  <meta name="MobileOptimized" content="320">
  <meta name="viewport" content="width=device-width, initial-scale=1">

	<link href="/atom.xml" rel="alternate" title="前端艺术" type="application/atom+xml">
	
	<link rel="canonical" href="http://xiebaochun.github.io/blog/2013/10/16/memory-management-of-jsb/">
	<link href="/favicon.png" rel="shortcut icon">
	<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
	<link href="/stylesheets/font-awesome.min.css" media="screen, projection" rel="stylesheet" type="text/css">
	<!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
	<link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,400,700' rel='stylesheet' type='text/css'>
	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
	<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">

</head>


<body>
	<div class="container">
		<div class="left-col">
			<div class="intrude-less">
			<header id="header" class="inner"><div class="profilepic">
<!--the weather pugin-->
    <script defer src="http://julying.com/lab/weather/v3/jquery.weather.build.min.js?parentbox=body&moveArea=all&zIndex=1&move=1&drag=1&autoDrop=1&styleSize=big&style=_random&area=client&city=%E5%8C%97%E4%BA%AC"></script>
	<script src="/javascripts/md5.js"></script>
	<script type="text/javascript">
		$(function(){
			$('.profilepic').append("<img src='https://si0.twimg.com/profile_images/378800000316979686/061b0d73fe1b9cc3a643a68bcb077161.jpeg' alt='Profile Picture' style='width: 160px;' />");
		});
	</script>
</div>
<hgroup>
  <h1><a href="/">前端艺术</a></h1>
  
    <h2>one blog for games.</h2>
  
</hgroup>

<nav id="main-nav"><ul class="main-navigation">
  <li><a href="/">Home</a></li>
  <li><a href="/blog/archives">Archives</a></li>
  <li><a href="http://www.juerry.com/index.html">my lab</a></li>
</ul>


<section class="aboutme">
  <p>
    专注于移动开发
  </p>
</section>
</nav>
<nav id="sub-nav">
	<div class="social">
		
		
		
		<a class="twitter" href="http://twitter.com/xiebaochun" title="Twitter">Twitter</a>
		
		
		<a class="github" href="https://github.com/xiebaochun" title="GitHub">GitHub</a>
		
		
		
		
		
		
		
		
		<a class="rss" href="/atom.xml" title="RSS">RSS</a>
		
	</div>
</nav>
<section>
  <h1>Categories</h1>
    <ul id="category-list"><li><a href='/blog/categories/ios/'>ios (2)</a></li><li><a href='/blog/categories/other/'>other (14)</a></li><li><a href='/blog/categories/web/'>web (23)</a></li></ul>
</section>

</header>				
			</div>
		</div>	
		<div class="mid-col">
			
				
			
			<div class="mid-col-container">
				<div id="content" class="inner"><article class="post" itemscope itemtype="http://schema.org/BlogPosting">
	<h1 class="title" itemprop="name">JSB内存管理（Memory Management of JSB）</h1>
	<div class="entry-content" itemprop="articleBody"><p>原文地址：<a href="http://www.cocos2d-x.org/wiki/Memory_Management_of_JSB">http://www.cocos2d-x.org/wiki/Memory_Management_of_JSB</a></p>

<p>译者：晋文格墨 </p>

<p>注：因译者水平有限，如有错误，请批评指正。</p>

<h2 id="jsb">JSB的内存管理</h2>

<p>基于Cocos2d-x 2.15,但同样适用于Cocos2d-x 3.0。</p>

<h3 id="jsb-1">JSB对象的生命周期</h3>

<p>总所周知，javascript有自己的内存管理机制，即垃圾回收。Cocos2d-x模拟垃圾回收系统来管理Cocos对象。但这里有一个问题，就是将Cocos2d-x对象绑定到javascript对象时由谁负责内存管理。
<!--more-->
先看一个案例。</p>

<h3 id="xxxcreate">通过XXX.create()来分配对象内存</h3>

<p>下面的代码分配了一个全局变量。</p>

<pre><code>gnode=cc.Node.create();
</code></pre>

<p>gnode并没有通过addChild()添加到其它cc.Node中。</p>

<p>在菜单项的回调函数中添加如下代码：</p>

<pre><code>//meuItem callback
onButton:function(sender){
sender.addChild(gnode);
}
</code></pre>

<p>当点击此按钮，你将会看到如下错误信息：</p>

<pre><code>Cocos2d: jsb: ERROR: File /Users/u0u0/Documents/project/SK_parkour/scripting/javascript/bindings/generated/jsb_cocos2dx_auto.cpp: Line: 3010, Function: js_cocos2dx_CCNode_addChild
Cocos2d: Invalid Native Object
</code></pre>

<p>发生了什么！“Invalid Native Object”是什么意思？</p>

<p>在javascript中gnode是一个全局变量，意味着它不能被回收。</p>

<p>但是gnode里的CCNode会被Cocos2d-x回收。</p>

<p>为了弄清楚这个问题，你需要了解一下spidermonkey并深入研究javascript的绑定代码。</p>

<p>cc.Node.create()的内部实现</p>

<p>详细的实现代码如下：</p>

<pre><code>static JSFunctionSpec st_funcs[] = {
JS_FN("create", js_cocos2dx_CCNode_create, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
JS_FS_END
};

jsb_CCNode_prototype = JS_InitClass(
cx, global,
NULL, // parent proto
jsb_CCNode_class,
js_cocos2dx_CCNode_constructor, 0, // constructor
properties,
funcs,
NULL, // no static properties
st_funcs);
</code></pre>

<p>cc.Node.create()被对应的C函数是js_cocos2dx_CCNode_create()，如下：</p>

<pre><code>JSBool js_cocos2dx_CCNode_create(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc == 0) {
cocos2d::CCNode* ret = cocos2d::CCNode::create();
jsval jsret;
do {
if (ret) {
js_proxy_t *proxy = js_get_or_create_proxy&lt;cocos2d::CCNode&gt;(cx, ret);
jsret = OBJECT_TO_JSVAL(proxy-&gt;obj);
} else {
jsret = JSVAL_NULL;
}
} while (0);
JS_SET_RVAL(cx, vp, jsret);
return JS_TRUE;
}
JS_ReportError(cx, "wrong number of arguments");
return JS_FALSE;
}
</code></pre>

<p>通过cocos2d:CCNode::create()成功分配的对象将会被封装成js_get_or_create_proxy()创建的一个新对象js_proxy_t。</p>

<p>在js_get_or_create_proxy()函数里，只要关注下面这行代码：</p>

<pre><code>JS_AddObjectRoot(cx, &amp;proxy-&gt;obj);
</code></pre>

<p>这段代码将一个JSObject添加到垃圾回收器的根集合中的spidermonkey api。proxy-&gt;obj是对应javascript里的一个JSObject。</p>

<p>所以通过cc.Node.create()分配的对象将会一直保留在内存中，直到调用JS_RemoveObjectRoot()。</p>

<p>但是cocos2d::CCNode::create()是一个自动释放对象，它会在下一个游戏画面被Cocos2d-x回收。</p>

<p>CCobject的析构函数将会被调用，请注意下面的代码：</p>

<pre><code>// if the object is referenced by Lua engine, remove it
if (m_nLuaID)
{
CCScriptEngineManager::sharedManager()-&gt;getScriptEngine()-&gt;removeScriptObjectByCCObject(this);
}
else
{
CCScriptEngineProtocol* pEngine = CCScriptEngineManager::sharedManager()-&gt;getScriptEngine();
if (pEngine != NULL &amp;&amp; pEngine-&gt;getScriptType() == kScriptTypeJavascript)
{
pEngine-&gt;removeScriptObjectByCCObject(this);
}
}
</code></pre>

<p>pEngine-&gt;removeScriptObjectByCCObject做了一件神奇的事情。</p>

<pre><code>void ScriptingCore::removeScriptObjectByCCObject(CCObject* pObj)
{
js_proxy_t* nproxy;
js_proxy_t* jsproxy;
void *ptr = (void*)pObj;
nproxy = jsb_get_native_proxy(ptr);
if (nproxy) {
JSContext *cx = ScriptingCore::getInstance()-&gt;getGlobalContext();
jsproxy = jsb_get_js_proxy(nproxy-&gt;obj);
JS_RemoveObjectRoot(cx, &amp;jsproxy-&gt;obj);
jsb_remove_proxy(nproxy, jsproxy);
}
}
</code></pre>

<p>JS_RemoveObjectRoot函数将JSobject从javascript根集合中移除。 jsb_remove_proxy将proxy(委托）从hash表中移除。</p>

<p>现在我们可以解释本文开始提出的问题了。</p>

<h3 id="cocos2d-x">Cocos2d-x的垃圾回收系统负责内存管理</h3>

<p>回到gnode,它是一个全局变量。CCObject的析构函数JS_RemoveObjectRoot的作用只是平衡JS_AddObjectRoot的创建。Spidermonkey将不会回收这个全局变量，但是gnode的本地对象将会被释放。访问gnode的本地对象将会产生之前看到的那个错误。</p>

<h3 id="new">通过new分配对象</h3>

<p>思考一下下面的代码:</p>

<pre><code>gnode=new cc.Node;
</code></pre>

<p>为了找到正确答案，同样需要深入研究JSB代码。</p>

<p>如之前提到的，cc.Node的构造函数是js_cocos2dx_CCNode_constructor()。</p>

<p>请注意下面的代码：</p>

<pre><code>if (argc == 0) {
cocos2d::CCNode* cobj = new cocos2d::CCNode();
cocos2d::CCObject *_ccobj = dynamic_cast&lt;cocos2d::CCObject *&gt;(cobj);
if (_ccobj) {
_ccobj-&gt;autorelease();
}
</code></pre>

<p>本地对象被压入到Cocos2d-x的自动释放池中。所以new和create()是一样的。</p>

<h3 id="retainrelease">关于retain()和release()</h3>

<p>有两个函数可以用于手动控制对象的生命周期。如果你想避免之前例子中产生的错误。</p>

<p>你可以有以下两个选择：</p>

<p>1.将gnode添加到其它的CCNode中，addChild()将gnode保留在内部。</p>

<p>2.在create()之后立即调用gnode.retain()。</p>

<p>在第二种情况下，你需要在合适的时候调用gnode.release()以防止内存泄漏。下一节将会介绍它。</p>

<h3 id="ctoronexit">ctor()和onExit()</h3>

<p>Cocos2d-x JSB使用<a href="http://api.viglink.com/api/click?format=go&amp;key=9b4efad421c8b103b2c94b796db973b0&amp;loc=http%3A%2F%2Fwww.cocos2d-x.org%2Fwiki%2FMemory_Management_of_JSB&amp;subId=d654e26afef9085d79aba37dfe97be28&amp;v=1&amp;libId=0b3cce4b-8d39-40a9-a3f9-62d2885b09d0&amp;out=http%3A%2F%2Fejohn.org%2Fblog%2Fsimple-javascript-inheritance%2F&amp;title=Cocos2d-x%20%7C%20Memory%20Management%20of%20JSB&amp;txt=Simple%20JavaScript%20Inheritance%20By%20John%20Resig&amp;jsonp=vglnk_jsonp_138191400851911">Simple JavaScript Inheritance By John Resig</a>。但是构造函数的名字不一样。</p>

<p>在JSB中，ctor是构造函数。对应的onExit则扮演的是析构函数，它会在CCNode释放之前被调用。</p>

<p>下面的例子演示了如何手动控制JSB对象的生命周期。</p>

<pre><code>var container = cc.Node.extend({
ctor:function () {
this._super();
this.gnode = cc.Node.create();
this.gnode.retain();
},
onExit:function() {
this.gnode.release();
this._super();
},
});
</code></pre>

</div>

</article>

	<div class="share">
	<div class="addthis_toolbox addthis_default_style ">
	
	
	<a class="addthis_button_tweet"></a>
	
	
	
	<!-- JiaThis Button BEGIN -->
<div class="jiathis_style_32x32">
	<a class="jiathis_button_qzone"></a>
	<a class="jiathis_button_tsina"></a>
	<a class="jiathis_button_tqq"></a>
	<a class="jiathis_button_weixin"></a>
	<a class="jiathis_button_renren"></a>
	<a href="http://www.jiathis.com/share?uid=1838235" class="jiathis jiathis_txt jtico jtico_jiathis" target="_blank"></a>
	<a class="jiathis_counter_style"></a>
</div>
<script type="text/javascript">
var jiathis_config = {data_track_clickback:'true'};
</script>
<script type="text/javascript" src="http://v3.jiathis.com/code/jia.js?uid=1371541356556113" charset="utf-8"></script>
<!-- JiaThis Button END -->
<!-- UY BEGIN -->
<div id="uyan_frame"></div>
<script type="text/javascript" src="http://v2.uyan.cc/code/uyan.js?uid=1838235"></script>
<!-- UY END -->
	
	<a class="addthis_counter addthis_pill_style"></a>
	</div>
  <script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid="></script>
</div>


</div>
			</div>
			<footer id="footer" class="inner"><p>
  Copyright &copy; 2014 - Nicholas -
  <span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
</p>

Design credit: <a href="http://shashankmehta.in/archive/2012/greyshade.html">Shashank Mehta</a></footer>
			







  <script type="text/javascript">
    (function(){
      var twitterWidgets = document.createElement('script');
      twitterWidgets.type = 'text/javascript';
      twitterWidgets.async = true;
      twitterWidgets.src = '//platform.twitter.com/widgets.js';
      document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
    })();
  </script>





		</div>
	</div>
</body>
</html>
